package com.lu.utils;

import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;


/**
 * 关键字匹配处理类
 *
 * @Author zhanglu
 * @create 2020/4/1 18:56
 *  https://www.cnblogs.com/zengda/p/4622111.html
 */
@Slf4j
public class KeyWordUtil {

    //最小匹配规则
    public static final int MINMATCHTYPE = 1;
    //最大匹配规则
    public static final int MAXMATCHTYPE = 2;
    //结束符
    public static final String END = "EOF";
    //map
    //public static ConcurrentHashMap<String, String> maps = new ConcurrentHashMap<>();
    public static HashMap<String, String> maps = new HashMap<>();

    //拼音和关键字(汉字) 之间的映射关系 Map<拼音, 汉字>
    public static HashMap<String, Set<String>> pinyinWordsMap = new HashMap<>();

    /**
     * 读取resources路径下关键字配置文件
     *
     * @param filePath
     * @return
     */
    public static Set<String> readKeySetFromConfigByLine(String filePath) {

        if (StringUtils.isEmpty(filePath)) {
            return null;
        }
        Set<String> keyWordSet = new HashSet<String>();
        BufferedReader reader = null;
        String temp = null;
        int line = 1;
        try {
            ClassPathResource classPathResource = new ClassPathResource(filePath);
            InputStream inputStream = classPathResource.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            while ((temp = reader.readLine()) != null) {
                //System.out.println("line" + line + ":" + temp);
                keyWordSet.add(temp);
                line++;
            }
        } catch (Exception e) {
            log.error("KeyWordUtil-readKeySetFromConfigByLine异常:", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {
                    log.error("KeyWordUtil-readKeySetFromConfigByLine异常:", e);
                }
            }
        }
        return keyWordSet;
    }

    /**
     * 构建关键字匹配树
     *
     * @param keyWordSet
     */
    public static void addKeyWordToHashMap(Set<String> keyWordSet) {
        String key = null;
        Map tempMap = null;
        Map<String, String> newWordMap = null;

        Iterator<String> iterator = keyWordSet.iterator();
        //迭代关键字SET
        while (iterator.hasNext()) {
            //关键字
            key = iterator.next();
            //生成处理map
            //tempMap = wordMap;
            tempMap = maps;
            //迭代关键字字符串
            for (int i = 0; i < key.length(); i++) {
                //获取char
                char keyChar = key.charAt(i);
                Object words = tempMap.get(keyChar);

                //如果存在直接赋值
                if (words != null) {
                    tempMap = (Map) words;
                    if ("3".equals(tempMap.get(END))) {
                        tempMap.put(END, "0");
                    }
                } else {
                    newWordMap = new HashMap<String, String>(2);
                    newWordMap.put(END, "0");     //不是最后一个
                    tempMap.put(keyChar, newWordMap);
                    tempMap = newWordMap;
                }

                if (i == key.length() - 1) {
                    tempMap.put(END, "1");    //最后一个
                }
            }
        }
    }

    public static void delKeyWordToHashMap(Set<String> keyWordSet) {
        //Map<String, String> wordMap = new HashMap(keyWordSet.size() * 2);
        String key = null;
        Map tempMap = null;
        Map<String, String> newWordMap = null;

        Iterator<String> iterator = keyWordSet.iterator();
        //迭代关键字SET
        while (iterator.hasNext()) {
            //关键字
            key = iterator.next();
            //生成处理map
            //tempMap = wordMap;
            tempMap = maps;

            for (int i = 0; i < key.length(); i++) {
                //获取char
                char keyChar = key.charAt(i);
                Object words = tempMap.get(keyChar);

                //如果存在直接赋值
                if (words != null) {
                    tempMap = (Map) words;
                    if ("1".equals(tempMap.get(END))) {//最后一个
                        tempMap.put(END, "3");//删除
                        break;
                    } else {
                        if (tempMap.size() == 2) {
                            tempMap.put(END, "3");//删除
                            break;
                        }
                    }

                }
                continue;
            }
        }

        //return wordMap;
    }

    /**
     * 检查文字中是否包含关键字符，检查规则如下：<br>
     */
    public static int checkKeyWord(String txt, int beginIndex, int matchType, Map wordMap) {
        boolean flag = false;
        int matchFlag = 0;
        char word = 0;
        Map nowMap = wordMap;

        for (int i = beginIndex; i < txt.length(); i++) {
            word = txt.charAt(i);
            nowMap = (Map) nowMap.get(word);     //获取指定key
            if (nowMap != null && !"3".equals(nowMap.get(END))) {     //存在，则判断是否为最后一个
                matchFlag++;     //找到相应key，匹配标识+1
                if ("1".equals(nowMap.get(END))) {       //如果为最后一个匹配规则,结束循环，返回匹配标识数
                    flag = true;       //结束标志位为true
                    if (MINMATCHTYPE == matchType) {    //最小规则，直接返回,最大规则还需继续查找
                        break;
                    }
                }
            } else {     //不存在，直接返回
                break;
            }
        }
        if (!flag) {
            matchFlag = 0;
        }
        return matchFlag;
    }


    /**
     * 获取关键字
     *
     * @param txt
     * @param matchType
     * @return
     */
    public static Set<String> getKeyWord(String txt, int matchType, Map wordMap) {
        Set<String> keyWordList = new HashSet<String>();

        for (int i = 0; i < txt.length(); i++) {
            int length = checkKeyWord(txt, i, matchType, wordMap);
            if (length > 0) {
                keyWordList.add(txt.substring(i, i + length));
                i = i + length - 1;
            }
        }

        return keyWordList;
    }


    /**
     * 判断文字是否包含关键字符
     */
    public static boolean isContaintKeyWork(String txt, int matchType, Map wordMap) {
        boolean flag = false;
        for (int i = 0; i < txt.length(); i++) {
            int matchFlag = checkKeyWord(txt, i, matchType, wordMap); //判断是否包含关键字符
            if (matchFlag > 0) {    //大于0存在，返回true
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 替换关键字字符
     *
     * @param txt
     * @param matchType
     * @param replaceChar 代替字节或字符串
     * @param replaceType 0 为单个字符替换 1为字符串替换
     * @param wordMap
     * @return
     */
    public static String replaceKeyWork(String txt, int matchType, String replaceChar, int replaceType, Map wordMap) {
        String resultTxt = txt;
        Set<String> set = getKeyWord(txt, matchType, wordMap);     //获取所有的关键词
        Iterator<String> iterator = set.iterator();
        String word = null;
        String replaceString = null;
        while (iterator.hasNext()) {
            word = iterator.next();
            if (replaceType == 0) {
                replaceString = getReplaceChars(replaceChar, word.length());
                resultTxt = resultTxt.replaceAll(word, replaceString);
            } else {
                resultTxt = resultTxt.replaceAll(word, replaceChar);
            }
        }
        return resultTxt;
    }

    /**
     * 获取替换字符串
     *
     * @param replaceChar
     * @param length
     * @return
     * @version 1.0
     */
    public static String getReplaceChars(String replaceChar, int length) {
        String resultReplace = replaceChar;
        for (int i = 1; i < length; i++) {
            resultReplace += replaceChar;
        }

        return resultReplace;
    }

    /**
     * 更新(新增&修改)
     *  维护：
     *      - 前缀树：汉字
     *      - 前缀树：拼音
     *      - 拼音与汉字的映射
     *
     * @param keywords   关键词
     */
    public static void setPrefixTreeMap(Set<String> keywords){
        if(keywords == null){
            return;
        }
        //维护前缀树：汉字
        addKeyWordToHashMap(keywords);
        //维护前缀树：拼音，维护拼音汉字映射
        keywords.stream().forEach(keyword -> setPinyinWordsMap(keyword));
    }

    /**
     * 删除
     *  维护：
     *      - 前缀树：汉字
     *      - 前缀树：拼音
     *      - 拼音与汉字的映射
     * @param keywords 关键词
     */
    public static void deletePrefixTreeMap(Set<String> keywords){
        if(keywords == null){
            return;
        }
        //维护前缀树：汉字
        delKeyWordToHashMap(keywords);
        //维护前缀树：拼音，维护拼音汉字映射
        keywords.stream().forEach(keyword -> deletePinyinWordsMap(keyword));
    }

    /**
     * 更新（新增和修改）
     *  维护映射和前缀树
     * @param keyword   关键词
     */
    public static void setPinyinWordsMap(String keyword){
        String fullSpell = Pinyin4jUtil.getFullSpell(keyword);
        Set<String> sets = pinyinWordsMap.get(fullSpell);
        if(ObjectUtil.isEmpty(sets)){
            sets = new HashSet<>();
        }
        sets.add(keyword);
        pinyinWordsMap.put(fullSpell, sets);
        HashSet<String> fullSpellSets = new HashSet<>();
        fullSpellSets.add(fullSpell);
        addKeyWordToHashMap(fullSpellSets);   //维护关键词拼音的前缀树
    }

    /**
     * 删除
     *  维护映射和前缀树
     * @param keyword 关键词
     */
    public static void deletePinyinWordsMap(String keyword){
        String fullSpell = Pinyin4jUtil.getFullSpell(keyword);
        Set<String> sets = pinyinWordsMap.get(fullSpell);
        if(ObjectUtil.isEmpty(sets)){
            pinyinWordsMap.remove(fullSpell);
            HashSet<String> fullSpellSets = new HashSet<>();
            fullSpellSets.add(fullSpell);
            delKeyWordToHashMap(fullSpellSets);   //维护关键词拼音的前缀树
            return;
        }
        sets.remove(keyword); //存在则删除
        if(sets.size() == 0){
            pinyinWordsMap.remove(fullSpell);
            HashSet<String> fullSpellSets = new HashSet<>();
            fullSpellSets.add(fullSpell);
            delKeyWordToHashMap(fullSpellSets);   //维护关键词拼音的前缀树
        }else{
            pinyinWordsMap.put(fullSpell, sets);
        }
    }

    /**
     * 若keywords中包含拼音，则把拼音转成汉字，返回一个新的不包含拼硬的set集合
     * @param keywords
     * @return
     */
    public static Set<String> getNewKeyWordsBySpell(Set<String> keywords){
        if(ObjectUtil.isEmpty(keywords)){
            return null;
        }
        Set<String> newKeywords = new HashSet<>();
        keywords.stream().forEach(keyWord -> {
            if(Validator.isChinese(keyWord)){   //是否是拼音
                newKeywords.add(keyWord);
            }else {
                Set<String> strings = KeyWordUtil.pinyinWordsMap.get(keyWord);
                if(ObjectUtil.isEmpty(strings)){
                    return;
                }
                newKeywords.addAll(strings);
            }
        });
        return newKeywords;
    }

    public static void main(String[] args) {
//        Set<String> sets = readKeySetFromConfigByLine("dictionary.txt");
//
//        System.out.println("关键词的数量：" + sets.size());
//        addKeyWordToHashMap(sets);
//        System.out.println("关键词结构：" + JSON.toJSONString(maps));
//
//        Set<String> testSet = new HashSet<String>();
//        testSet.add("新增");
//        testSet.add("新华");
//        testSet.add("大保健");
//        testSet.add("大小键");
//        addKeyWordToHashMap(testSet);
//        System.out.println("修改后关键词结构：" + JSON.toJSONString(maps));
//
//        String string = "啦啦啦啦新增哈哈新华啦啦大小键啦啦大保健大小哈哈大小龙";
//        System.out.println("待检测语句字数：" + string.length());
//        long beginTime = System.currentTimeMillis();
//        Set<String> set = getKeyWord(string, 2, maps);
//        long endTime = System.currentTimeMillis();
//        System.out.println("语句中包含关键词的个数为：" + set.size() + "。包含：" + set);
//        System.out.println("总共消耗时间为：" + (endTime - beginTime));
//
//
//        Set<String> delSet = new HashSet<String>();
//        delSet.add("新增");
//        delSet.add("大小键");
//        delKeyWordToHashMap(delSet);
//
//        System.out.println("关键词结构：" + JSON.toJSONString(maps));
//        Set<String> delset = getKeyWord(string, 2, maps);
//        System.out.println("语句中包含关键词的个数为：" + delset.size() + "。包含：" + delset);
//
//        System.out.println(string);
//        String string2 = string;
//        System.out.println(isContaintKeyWork(string, 1, maps));
//        System.out.println(replaceKeyWork(string, 2, "新华书店", 0, maps));
//        System.out.println(replaceKeyWork(string2, 2, "新华书店", 1, maps));
//
//        testSet.add("大小龙");
//        addKeyWordToHashMap(testSet);
//        System.out.println("修改后关键词结构：" + JSON.toJSONString(maps));
//
//        Set<String> addset = getKeyWord(string, 2, maps);
//        System.out.println("语句中包含关键词的个数为：" + addset.size() + "。包含：" + addset);
//        long beginTime2 = System.currentTimeMillis();
//        for (String keyword : sets) {
//            //Matcher matcher = Pattern.compile("[" + keyword + "]").matcher(string);
//            Matcher matcher = Pattern.compile(keyword).matcher(string);
//            if (matcher.find()) {
//                System.out.println("正则匹配=" + matcher.group(0));
//            }
//        }
//        long endTime2 = System.currentTimeMillis();
//        System.out.println("正则所消耗时间为：" + (endTime2 - beginTime2));
//
//
//        String match = "";
//        for (String keyword : sets) {
//            match = match + "|" + keyword;
//            //Matcher matcher = Pattern.compile("[" + keyword + "]").matcher(string);
//        }
//        match = match.substring(1, match.length());
//        match = match.substring(0, match.length() - 1);
//        System.out.println("match==" + match);
//        long beginTime3 = System.currentTimeMillis();
//        Matcher matcher = Pattern.compile(match).matcher(string);
//        if (matcher.find()) {
//            System.out.println("正则2匹配=" + matcher.group(0));
//        }
//        long endTime3 = System.currentTimeMillis();
//        System.out.println("正则2所消耗时间为：" + (endTime3 - beginTime3));
//


        //拼音关键词 测试
        HashSet<String> sss = new HashSet<>();
        sss.add("一扇");
        sss.add("一闪");
        sss.add("一山");
        sss.add("你好");
        sss.add("hello 树先生");

        setPrefixTreeMap(sss);

        Set<String> k1 = KeyWordUtil.getKeyWord("tongzhinihao,qingbangwodakaiyishan窗户", 2, KeyWordUtil.maps);
        Set<String> r1= getNewKeyWordsBySpell(k1);

        HashSet<String> s = new HashSet<>();
        s.add("一扇");
        s.add("一闪");
        s.add("一山");
        deletePrefixTreeMap(s);

        Set<String> k2 = KeyWordUtil.getKeyWord("同志你好，请帮我打开yishan窗户", 2, KeyWordUtil.maps);
        Set<String> r2= getNewKeyWordsBySpell(k2);

        System.out.println(1);

    }

}
